package com.polaris.live.viewmodel.live

import android.content.Context
import androidx.annotation.CallSuper
import androidx.core.os.bundleOf
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.blankj.utilcode.util.AppUtils
import com.blankj.utilcode.util.NetworkUtils
import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.ktx.Firebase
import com.polaris.live.common.cacha.CacheUtil
import com.polaris.live.common.cacha.UserManager
import com.polaris.live.common.constant.AppConst
import com.polaris.live.common.constant.CommonConst
import com.polaris.live.common.manager.CoroutineScopeManager
import com.polaris.live.common.mvvm.viewmodel.BaseViewModel
import com.polaris.live.common.network.AppException
import com.polaris.live.common.network.request
import com.polaris.live.common.network.requestLoading
import com.polaris.live.common.network.requestNoCheck
import com.polaris.live.common.network.state.ResultState
import com.polaris.live.common.util.LogExt
import com.polaris.live.common.util.ToastGravity
import com.polaris.live.constant.EventConst
import com.polaris.live.constant.LiveConst
import com.polaris.live.constant.LiveConst.CallAnswerType
import com.polaris.live.constant.LiveConst.CallCloseType
import com.polaris.live.constant.LiveConst.LivePage
import com.polaris.live.constant.LiveConst.Page
import com.polaris.live.dialog.GlobalDialogManager
import com.polaris.live.im.sfs.bean.live.CallSignalingMessage
import com.polaris.live.interface_bag.S3UploadCallback
import com.polaris.live.net.gameService
import com.polaris.live.net.liveService
import com.polaris.live.net.payerService
import com.polaris.live.net.userService
import com.polaris.live.popup.GlobalPopupManager
import com.polaris.live.resp.back_resp.CallStartResp
import com.polaris.live.resp.back_resp.CallVo
import com.polaris.live.resp.back_resp.GameGroupVoResp
import com.polaris.live.resp.back_resp.GameRankResp
import com.polaris.live.resp.back_resp.InfoOtherResp
import com.polaris.live.resp.back_resp.PayerLampLotteryResp
import com.polaris.live.resp.back_resp.ScreenshotStrategyVo
import com.polaris.live.resp.bean.SendGiftBackBean
import com.polaris.live.resp.go_resp.CallAnswerBean
import com.polaris.live.resp.go_resp.CallEndBean
import com.polaris.live.resp.go_resp.LiveFollowBean
import com.polaris.live.resp.go_resp.OtherUserIdBean
import com.polaris.live.resp.go_resp.ScreenshotLoadBean
import com.polaris.live.resp.go_resp.SendGiftBean
import com.polaris.live.ui.game.GameFromType
import com.polaris.live.ui.game.GameUtils
import com.polaris.live.utils.BitwiseUtils
import com.polaris.live.utils.image.ImageUtils
import com.polaris.live.utils.postEvent
import com.polaris.live.utils.zego.LiveEngineManage
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

/**
 * LiveVideoViewModel
 *
 * @author Created by 半仙 on 2023/9/8/008 14:44
 */
open class LiveViewModel : BaseViewModel() {

    //应答 - 接听还是拒绝
    val callAnswerLiveData = MutableLiveData<Pair<CallSignalingMessage, CallStartResp>>()
    fun answerCall(
        message: CallSignalingMessage,
        @CallAnswerType operation: Int,
        needLoading: Boolean = false,
    ) {
        invokeAnswerCall(message, operation, needLoading, 0)
    }

    private fun invokeAnswerCall(
        message: CallSignalingMessage,
        operation: Int,
        needLoading: Boolean,
        retryCount: Int,
    ) {
        val success: (CallStartResp) -> Unit = {
            if (operation == LiveConst.AnswerType.AGREE) {
                callAnswerLiveData.value = message to it
            }
        }
        val error: (AppException) -> Unit = {
            if (it.errCode != CommonConst.ErrorCode.CALL_END) {
                //如果是同意失败重新调用一次
                if (operation == LiveConst.AnswerType.AGREE && retryCount < 1) {
                    invokeAnswerCall(message, operation, needLoading, retryCount + 1)
                }
            }
        }

        if (needLoading) {
            requestLoading(
                { liveService.callAnswer(CallAnswerBean(message.callId, operation)) },
                success,
                error
            )
        } else {
            request(
                { liveService.callAnswer(CallAnswerBean(message.callId, operation)) },
                success,
                error
            )
        }
    }

    //通话结束
    val callEndLiveData = MutableLiveData<ResultState<CallVo>>()
    fun callEnd(
        callId: Long,
        @CallCloseType endType: Int,
        closeUserId: Long? = null,
        needLoading: Boolean = false,
    ) {
        when (endType) {
            LiveConst.CloseType.TIMEOUT, LiveConst.CloseType.ERROR -> {
                LiveEngineManage.getHandler()?.uploadLog()

                Firebase.analytics.postEvent(
                    EventConst.ZEGO_CALL_ERROR,
                    bundleOf(
                        "callId" to callId,
                        "loginRoom" to LiveEngineManage.getHandler()?.isLoginRoom(),
                        "network" to NetworkUtils.isConnected(),
                        "foreground" to AppUtils.isAppForeground()
                    )
                )
            }

            else -> {}
        }

        if (needLoading) {
            requestLoading(
                { liveService.callEnd(CallEndBean(callId, endType, closeUserId)) },
                callEndLiveData
            )
        } else {
            request(
                { liveService.callEnd(CallEndBean(callId, endType, closeUserId)) },
                callEndLiveData
            )
        }
    }

    //关注
    val followerLiveData = MutableLiveData<ResultState<Any>>()
    fun addFollower(otherId: Long) {
        request({ userService.addFollower(OtherUserIdBean(otherId)) }, followerLiveData)
    }

    //直播关注
    val liveFollowLiveData = MutableLiveData<ResultState<Any>>()
    fun addLiveFollow(otherId: Long, liveId: Long) {
        request({ liveService.liveFollow(LiveFollowBean(liveId, otherId)) }, liveFollowLiveData)
    }

    //获取用户信息
    val infoLiveData = MutableLiveData<ResultState<InfoOtherResp>>()
    fun getInfoUser(id: Long, vararg type: Long) {
        val bitType = BitwiseUtils.bitwiseOperation(type)
        request({ userService.getInfoOther(id, bitType) }, infoLiveData)
    }

    //礼物飘屏用户数据
    val giftInfoLiveData = MutableLiveData<ResultState<InfoOtherResp>>()
    fun getGiftInfoUser(id: Long) {
        val type = BitwiseUtils.bitwiseOperation(
            longArrayOf(
                UserManager.BASIC_INFORMATION,
                UserManager.LIVE_INFORMATION,
            )
        )
        request({ userService.getInfoOther(id, type) }, giftInfoLiveData)
    }

    //获取游戏列表
    val gameLiveData = MutableLiveData<ResultState<List<GameGroupVoResp>>>()
    fun getGameList(liveId: Long) {
        if (GameUtils.getLiveCacheGameList().isNullOrEmpty()) {
            request({ gameService.getGameList(GameFromType.LIVE) }, gameLiveData)
        } else {
            gameRank(liveId)
        }
    }

    //游戏排行榜
    val gameRankLiveData = MutableLiveData<ResultState<List<GameRankResp>>>()
    fun gameRank(liveId: Long) {
        if (GameUtils.getShowWinPop(liveId)) { //游戏列表不为空进行弹窗
            viewModelScope.launch {
                delay(GameUtils.MAX_LAST_POP_TIME)
                request({ gameService.getGameRank() }, gameRankLiveData)
            }
        }
    }

    /**
     * 送礼物
     * type是礼物类型
     * 其他的值就代表关注礼物的下标  因为需要这个下标去删除对应的消息
     */
    val sendFollowerLiveData = MutableLiveData<Int?>()
    val sendGiftLiveData = MutableLiveData<SendGiftBackBean>()
    fun sendGift(
        context: Context,
        sendGiftBean: SendGiftBean,
        type: Int,
        giftPic: String? = null,
        showGoVip: Boolean = true,
    ) {
        requestNoCheck({ liveService.sendGift(sendGiftBean) }, {
            sendGiftLiveData.value = SendGiftBackBean(sendGiftBean.backpackId, it.ext)
            sendFollowerLiveData.value = type
            if (!it.isSuccess()) {
                ToastGravity.showGravity(it.errorMessage)
                if (it.code == CommonConst.ErrorCode.SENDGIFT) {
                    GlobalPopupManager.showRechargePopup(context, this, isNeedBottom = true)
                }
                if (it.code == CommonConst.ErrorCode.GAME_BEANS) {
                    GlobalPopupManager.showExchangeGamePopup(context, this)
                }
                if (it.code == CommonConst.ErrorCode.VIP_LEVEL_INSUFFICIENT) {
                    GlobalDialogManager.showInsufficientVIPLevel(context, sendGiftBean.payload, giftPic, showGoVip)
                }
            }
        })
    }

    //截图上传
    private var screenshotJob: Job? = null
    fun launchScreenshot(
        @LivePage pageType: Int,
        liveId: Long,
        liveStartTime: Long,
        screenshotStrategy: ScreenshotStrategyVo,
    ) {
        val rules = screenshotStrategy.rules
        if (rules.isEmpty()) {
            return
        }

        //获取最大延迟
        val maxInterval = rules.entries.maxBy { it.key }.value * 1000

        screenshotJob = viewModelScope.launch {
            try {
                while (isActive) {
                    val startTime = System.currentTimeMillis()

                    withContext(CoroutineScopeManager.ioDispatcher) {
                        try {
                            takeScreenshot(pageType, liveId)
                        } catch (_: Throwable) {
                        }
                    }

                    val tickDuration = System.currentTimeMillis() - startTime

                    val interval =
                        computeNextDelay(liveStartTime, screenshotStrategy) ?: maxInterval
                    val delayTime = interval - tickDuration
                    if (delayTime > 0) {
                        delay(delayTime)
                    }
                }
            } finally {
                if (screenshotJob == coroutineContext[Job]) {
                    screenshotJob = null
                }
            }
        }
    }

    private fun computeNextDelay(
        liveStartTime: Long,
        screenshotStrategy: ScreenshotStrategyVo,
    ): Long? {
        //先计算当前是第几分钟
        val intervalMinute = (CacheUtil.getServerTime() - liveStartTime) / 1000 / 60

        return screenshotStrategy.rules[(intervalMinute + 1).toString()]?.let { it * 1000L }
    }

    private fun takeScreenshot(pageType: Int, liveId: Long) {
        val (type, sceneType) = if (pageType == Page.CALL) {
            AppConst.UpLoad.CALL_SCREENSHOT to CommonConst.Screenshot.CALL
        } else {
            AppConst.UpLoad.LIVE_SCREENSHOT to CommonConst.Screenshot.LIVE
        }
        takeScreenshot(type) {
            screenshotUpload(sceneType, liveId, it)
        }
    }

    private fun screenshotUpload(type: Int, objectId: Long, path: String) {
        request({ liveService.screenshotUpLoad(ScreenshotLoadBean(type, objectId, path)) }, {})
    }

    private var mScreenshotErrorTime: Int = 0
    fun takeScreenshot(type: Int, block: (String) -> Unit) {
        LiveEngineManage.getEngine()?.takePublishStreamSnapshot { errorCode, image ->
            if (errorCode == 0 && image != null) {

                if (ImageUtils.isBitmapEmpty(image)) {
                    if (mScreenshotErrorTime++ <= 3) {
                        viewModelScope.launch {
                            delay(1000)
                            takeScreenshot(type, block)
                        }
                    }
                    return@takePublishStreamSnapshot
                }

                mScreenshotErrorTime = 0

                ImageUtils.s3Upload(image, type, callback = object : S3UploadCallback {
                    override fun onSuccess(url: String, size: Pair<Int, Int>?) {
                        block(url)
                    }

                    override fun onFailure(t: Throwable) {
                        LogExt.logE("--net", t)

                        Firebase.analytics.postEvent(
                            "screenshot_error", bundleOf(
                                "errorMessage" to t.message
                            )
                        )
                    }
                })
            } else {
                Firebase.analytics.postEvent(
                    "screenshot_error", bundleOf(
                        "errorCode" to errorCode
                    )
                )
            }
        }
    }

    /**
     * 停止直播的轮询请求
     */
    @CallSuper
    open fun resetLiveJob() {
        screenshotJob?.cancel()
        screenshotJob = null
    }

    //神灯抽奖
    val mLampLotteryLiveData = MutableLiveData<ResultState<PayerLampLotteryResp>>()
    fun lampLottery(userId: Long, index: Long) {
        request({ payerService.getLampLottery(userId, index) }, mLampLotteryLiveData)
    }

}